#include "xSceneManager.h"
#include "xSceneNode.h"
#include "XRenderSystem.h"
#include "xCamera.h"
#include "xLogManager.h"
#include "XEngine.h"
#include "xMeshManager.h"
#include "xHelp.h"
#include "xRenderObject.h"
#include "xRenderQueue.h"
#include "xEntity3D.h"




namespace XE
{

	xSceneManager::xSceneManager(const xString& name)
		:m_Name(name.c_str()),m_pRootNode(NULL)
	{
		m_pRootNode = new xSceneNode(this,NULL,"RootNode");

		InitRenerQueue();


	}


	xSceneManager::~xSceneManager()
	{
		DestoryAllCamera();
		SafeDelete(m_pRootNode)
		DestoryAllRenderObject();
		DestoryAllRenderQueue();

	}

	xSceneNode* xSceneManager:: CreateSceneNodeImplement(const xString& name,xSceneNode* pParent)
	{
		return new xSceneNode(this,pParent,name);
	}




	xSceneNode*  xSceneManager::CreateSceneNode(const xString& name,xSceneNode* pParent)
	{
		xSceneNode* pNode = GetSceneNodeByName(name);
		if(pNode!=NULL)
		{
			xLogMessager::getSingleton().logMessage("Create Scenenode Fail,has same name node. name is"+name);
			return pNode;
		}
		if(pParent==NULL)
			pParent = m_pRootNode;
		pNode =   CreateSceneNodeImplement(name,pParent);
		if(pNode==NULL)
		{
			xLogMessager::getSingleton().logMessage("Create SceneNode Fail");
			return NULL;
		}
		m_SceneNodeMap.insert(std::make_pair(name,pNode));
		return pNode;

	}

	xSceneNode* xSceneManager::GetSceneNodeByName(const xString& name) const
	{
		SceneNodeMap::const_iterator it = m_SceneNodeMap.find(name);
		if(it==m_SceneNodeMap.end())
			return NULL;
		return it->second;

	}
	//----this will destory all child
	bool xSceneManager::DestorySceneNode(const xString& name)
	{
		SceneNodeMap::iterator it =  m_SceneNodeMap.find(name);
		if(it==m_SceneNodeMap.end())
			return false;
		delete it->second;
		m_SceneNodeMap.erase(it);
		return true;
	}


	bool xSceneManager::DestorySceneNode(xSceneNode* pNode)
	{
		if(pNode==NULL)
			return false;
		return DestorySceneNode(pNode->GetName());
	}



	xSceneNode*  xSceneManager::CreateSceneNode(xSceneNode* pParen)
	{ 

		static int SceneNodeNum = 0;
		if(pParen==NULL)
			pParen = m_pRootNode;
		xString nodename = "unNameSceneNode";
		nodename+=xHelp::IntToString(SceneNodeNum++);

		xSceneNode* pNode =  CreateSceneNodeImplement(nodename,pParen);
		if(pNode==NULL)
		{
			xLogMessager::getSingleton().logMessage("create Scene Node Fial");
			return NULL;
		}
		m_SceneNodeMap.insert(std::make_pair(nodename,pNode));

		return pNode;

	}







	xCamera* xSceneManager::GetCameraByName(const xString& name)
	{
		CameraMap::iterator it=  m_CameraMap.find(name);
		if(it==m_CameraMap.end())
			return NULL;
		return it->second;
	}

	xCamera* xSceneManager::CreateCamera(const xString& name,xSceneNode* pParaNode)
	{
		xCamera* pCamera =GetCameraByName(name);
		if(pCamera!=NULL)
		{
			xLogMessager::getSingleton().logMessage("Create Camera Fail has same Name Camera");
			return NULL;
		}
		if(pParaNode==NULL)
			pParaNode = m_pRootNode;
		pCamera = new xCamera(this,name,pParaNode);
		m_CameraMap.insert(std::make_pair(name,pCamera));
		return pCamera;
	}

	bool xSceneManager::DestoryCamera(const xString& name)
	{
		CameraMap::iterator it=  m_CameraMap.find(name);
		if(it!=m_CameraMap.end())
		{
			delete it->second;
			m_CameraMap.erase(it);
			return true;
		}
		return false;
	}


	void xSceneManager::DestoryAllCamera()
	{
		CameraMap::iterator it=  m_CameraMap.begin();
		CameraMap::iterator itend = m_CameraMap.end();
		for(;it!=itend;++it)
		{
			SafeDelete(it->second)
		}
		m_CameraMap.clear();

	}


	HRESULT xSceneManager::WalkScene(const xCamera* pCamera)
	{

		ClearRenderAllQueue();

		SceneNodeMap::iterator itbegin =  m_SceneNodeMap.begin();
		SceneNodeMap::iterator itend =  m_SceneNodeMap.end();

		for(;itbegin!=itend;++itbegin)
		{
			if(itbegin->second->isVisible()==false)
				continue;
			if(itbegin->second==NULL)
				continue;

			itbegin->second->updateRenderQueue(pCamera );

			//RenderObjectMapIterator RenderIt = itbegin->second->GetRenderObjectIterator();
			//while(RenderIt.hasMoreElements())
			//{
			//	xRenderObject* ptem = RenderIt.getNext();
			//	if(ptem==NULL)
			//		continue;
			//	xRenderQueueLevel level = ptem->GetRenderQueueLevel();
			//	ptem->_updateRenderQueue(m_RenderQueue[level]);

			////	m_RenderQueue[level]->AddRenderObject(ptem);

			//}
		}

		return true;
	}


	//------render one frame  from the Camera;
	bool xSceneManager::CaptureOneFrame(xCamera* pCamera)
	{

		XRenderSystem* pRender  = XEngine::getSingleton().GetRenderSystem();

		//pCamera->Translate(xVector3(0.0f,0.0,0.100f));
		//------
		int updatnodenum = 0;
		m_pRootNode->update(false,updatnodenum);


		WalkScene(pCamera);





		HRESULT hr = X_OK;
		hr = pRender->Clear(true,true,xColor(0.7f,0.7f,0.7f,1.0f));
		hr = pRender->BeginRender();


	/*	xMatrix viewMat;
		xMatrix proMat;
		xMatrix worldMat;
		xMath::MatrixIdentity(worldMat);
		xMath::BuildLookAtMatrix(viewMat,xVector3(1000.0f, 0.0f, -1000.0f),xVector3(0,0,0),xVector3(0,1,0));
		xMath::BuildProjectMatrix(proMat,PI/4.0f,1.33f,1.0f,5000.0f);*/
		pRender->SetViewMatrix(pCamera->GetViewMatrix());
		pRender->SetProjectMatrix(pCamera->GetProjectMatrix());
		//pRender->SetWorldMatrix(&worldMat);


		RenderAllQueue(pRender,pCamera);


	//	xMeshManager::getSingleton().Render(100);

		hr =  pRender->EndRender();

		return true;

	}


	xRenderObject* xSceneManager::CreateRenderObject(const  xRenderObjectCreateParameter& para)
	{

		xRenderObject* pRenderObj = GetRenderObject(para.GetName(),para.GetType());
		if(pRenderObj!=NULL)
		{
			xLogMessager::getSingleton().logMessage(\
				"Create RenderObect Fail, there has same type and name RenderObject ,name is"\
				+para.GetName()+"type is:"+para.GetType());
			return NULL;
		}

		xRenderObjectFactory* pFactory =   XEngine::getSingleton().GetRenderObjectFactory(para.GetType());
		if(pFactory	==NULL)
		{
			xLogMessager::getSingleton().logMessage("Create RenderObject Fail ,can't find same type factory");
			return NULL;
		}

		pRenderObj =   pFactory->CreateRenderObject(para,this);
		if(pRenderObj==NULL)
		{
			xLogMessager::getSingleton().logMessage("Factory create RenderObject Fail");
			return NULL;
		}

		RenderTypeMap::iterator  TypeIt = m_RenderObjectCollect.find(pRenderObj->GetType());
		if(TypeIt==m_RenderObjectCollect.end())
		{
			RenderObjectMap*  objmap = new RenderObjectMap();
			objmap->insert(std::make_pair(pRenderObj->GetName(),pRenderObj));
			m_RenderObjectCollect.insert(std::make_pair(pRenderObj->GetType(),objmap));

		}else
		{
			TypeIt->second->insert(std::make_pair(pRenderObj->GetName(),pRenderObj));

		}

		return pRenderObj;	

	}

	bool xSceneManager::DestoryRenderObject( xRenderObject* pObj)
	{
		if(pObj==NULL)
			return false;
		return DestoryRenderObject(pObj->GetName(),pObj->GetType());
	}

	bool xSceneManager::DestoryRenderObject(const xString& objName,const xString objtype)
	{
		RenderTypeMap::iterator  TypeIt = m_RenderObjectCollect.find(objtype);
		if(TypeIt==m_RenderObjectCollect.end())
		{
			return false;

		}else
		{
			RenderObjectMap::iterator it = TypeIt->second->find(objName);
			if(it!=TypeIt->second->end())
			{ 
				SafeDelete(it->second)
					TypeIt->second->erase(it);
				return true;
			}
			return false;

		}
		return true;
	}



	void xSceneManager::DestoryAllRenderObject()
	{
		RenderTypeMap::iterator  TypeIt = m_RenderObjectCollect.begin();
		for(;TypeIt!=m_RenderObjectCollect.end();++TypeIt)
		{

			if(TypeIt->second==NULL)
				continue;

			RenderObjectMap::iterator Objit = TypeIt->second->begin();
			for(;Objit!=TypeIt->second->end();++Objit)
			{
				delete Objit->second;
			}
			TypeIt->second->clear();

			delete TypeIt->second;

		}
		m_RenderObjectCollect.clear();
		return ;

	}

	xRenderObject* xSceneManager::GetRenderObject(const xString& objeName,const xString& typeName)
	{

		RenderTypeMap::iterator  TypeIt = m_RenderObjectCollect.find(typeName);
		if(TypeIt==m_RenderObjectCollect.end())
		{
			return NULL;

		}else
		{
			RenderObjectMap::iterator it = TypeIt->second->find(objeName);
			if(it!=TypeIt->second->end())
			{
				return it->second;
			}
			return NULL;

		}
		return NULL;
	}



	void xSceneManager::DestoryAllRenderQueue()
	{
		for(UINT level= xRQL_BACK_LEVEL;level<(UINT)xRQL_MAX_LEVEL-1;++level)
		{
			SafeDelete(m_RenderQueue[level])
		}

	}

	void  xSceneManager::InitRenerQueue()
	{
		for(UINT level=(UINT) xRQL_BACK_LEVEL;level<(UINT)xRQL_MAX_LEVEL-1;++level)
		{
			//SafeDelete(m_RenderQueue[level])
			m_RenderQueue[level]=new xRenderQueue((xRenderQueueLevel)level);
		}
	}

	void xSceneManager::RenderAllQueue(XRenderSystem* pRenderSystem,  xCamera* pCamera )
	{

		for(UINT level=(UINT) xRQL_BACK_LEVEL;level<(UINT)xRQL_MAX_LEVEL-1;++level)
		{
			m_RenderQueue[level]->Render(pRenderSystem, pCamera );

		}

		return;
	}

	void xSceneManager::ClearRenderAllQueue()
	{
		for(UINT level=(UINT) xRQL_BACK_LEVEL;level<(UINT)xRQL_MAX_LEVEL-1;++level)
		{
			m_RenderQueue[level]->Clear();

		}


	}



	xEntity3D*  xSceneManager::CreateEntity(const xString& EnityName,const xString& MeshName)
	{
	       XE::xEnity3DCreateParameter e3dpara(EnityName,MeshName,"");
		 return static_cast<xEntity3D*>( CreateRenderObject(e3dpara));
	}

	bool  xSceneManager::DestoryEntity(xEntity3D* pEntity)
	{
          if(pEntity==NULL)
			  return false;
		  return DestoryEntity(pEntity->GetName());
	}

	bool  xSceneManager::DestoryEntity(const xString& name)
	{
		return DestoryRenderObject(name,xEntity3D::RenderType);

	}

	xEntity3D*  xSceneManager::GetEntity3D(const xString& name)
	{
	   return static_cast<xEntity3D*>(GetRenderObject(name,xEntity3D::RenderType));

	}



	void	 xSceneManager::GetRayIntersectObjects(const xRay& ray,PickRenderObjectMap& rendervector)
	{
	
		RenderTypeMap::iterator RendermapIter = m_RenderObjectCollect.begin();
		for(;RendermapIter!=m_RenderObjectCollect.end();++RendermapIter)
		{

			RenderObjectMap::iterator objIt =   RendermapIter->second->begin();
			for(;objIt!=RendermapIter->second->end();++objIt)
			{
				xRenderObject* pRenderObje = objIt->second;
				if(pRenderObje==NULL)
					continue;
				const xBox* pworldBox =pRenderObje->GetWorldBox();
				if(pworldBox==NULL)
					continue;
				float dis = 0;
				if(xInterSect::IntersectRayAndBox(ray,*pworldBox,dis))
				{

					rendervector.insert(std::make_pair(dis,pRenderObje));
				}
				

			}

		}


		return ;

	}




}